﻿using System;
using System.Collections;
using System.Collections.Generic;
using Me.Prettyprint.Cassandra.Connection;
using Me.Prettyprint.Cassandra.Service;
using Me.Prettyprint.Cassandra.Serializers;
using Me.Prettyprint.Cassandra.Utils;
using Me.Prettyprint.Hector.Api;
using Me.Prettyprint.Hector.Api.Beans;
using Me.Prettyprint.Hector.Api.Query;
using Org.Apache.Cassandra.Thrift;
using Thrift;

namespace Me.Prettyprint.Cassandra.Model
{
    /// <summary>
    /// Default implementation of HColumn interface converts the thrift column 
    /// to Hector HColumn instances.
    /// </summary>
    /// <typeparam name="N"></typeparam>
    /// <typeparam name="V"></typeparam>
    public class HColumnImpl<N,V> : HColumn<N, V>
    {
        public HColumnImpl(N name, V value, long clock, Serializer<N> nameSerializer,  Serializer<V> valueSerializer) : this(nameSerializer, valueSerializer)
        {
            if (name == null || value == null)
            {
                throw new NullReferenceException("Name and Value cannot be null");
            }
            Column = new Column();
            Column.Name = nameSerializer.ToBytes(name);
            Column.Value = valueSerializer.ToBytes(value);
            Column.Timestamp = clock;
            Column.__isset.name = true;
            Column.__isset.value = true;
            Column.__isset.timestamp = true;
        }

        public HColumnImpl(N name, V value, long clock, int ttl, Serializer<N> nameSerializer, Serializer<V> valueSerializer) : this(name, value, clock, nameSerializer, valueSerializer)
        {
            SetTtl(ttl);
            Column.__isset.ttl = true;
        }

        public HColumnImpl(Column thriftColumn, Serializer<N> nameSerializer,
            Serializer<V> valueSerializer) : this(nameSerializer, valueSerializer)
        {
            if (thriftColumn == null)
            {
                throw new NullReferenceException("Column cannot be null");
            }
            Column = thriftColumn;
            Column.Name = thriftColumn.Name;
            Column.Value = thriftColumn.Value;
            Column.Timestamp = thriftColumn.Timestamp;
            Column.__isset.name = true;
            Column.__isset.value = true;
            Column.__isset.timestamp = true;
        }

        public HColumnImpl(Serializer<N> nameSerializer, Serializer<V> valueSerializer)
        {
            if (nameSerializer == null || valueSerializer == null)
            {
                throw new NullReferenceException("Name and Value serializer cannot be null");
            }
            NameSerializer = nameSerializer;
            ValueSerializer = valueSerializer;
            Column = new Column();
        }

        public HColumnImpl(N name, V value, long clock) : this(name, value, clock, SerializerTypeInferer.GetSerializer<N>(name), SerializerTypeInferer.GetSerializer<V>(value))
        {
        }
        protected Column Column
        {
            get;
            set;
        }
        public Serializer<N> NameSerializer
        {
            get;
            set;
        }
        public Serializer<V> ValueSerializer
        {
            get;
            set;
        }
        protected N CachedName
        {
            get;
            set;
        }
        private V CachedValue
        {
            get;
            set;
        }
        public int Ttl
        {
            get
            {
                return Column.Ttl;
            }
        }
        public N Name
        {
            get
            {
                if (Column.Name != null)
                {
                    if (CachedName == null)
                    {
                        CachedName = NameSerializer.FromBytes(Column.Name);
                    }
                    return CachedName;
                }
                else
                {
                    return default(N);
                }
            }
        }
        public V Value
        {
            get
            {
                if (Column.Value != null)
                {
                    if (null == CachedValue)
                    {
                        CachedValue = ValueSerializer.FromBytes(Column.Value);
                    }
                    return CachedValue;
                }
                else
                {
                    return default(V);
                }
            }
        }
        public long Clock
        {
            get
            {
                return Column.Timestamp;
            }
        }
        public HColumn<N, V> SetClock(long clock)
        {
            Column.Timestamp = clock;
            return this;
        }
        public HColumn<N,V> SetName(N name) 
        {
            if (name == null)
            {
                throw new NullReferenceException("Name cannot be null");
            }
            Column.Name = NameSerializer.ToBytes(name);
            CachedName = default(N);
            return this;
        }
        public HColumn<N, V> SetValue(V value) 
        {
            if (value == null)
            {
                throw new NullReferenceException("Value cannot be null");
            }
            Column.Value = ValueSerializer.ToBytes(value);
            CachedValue = default(V);
            return this;
        }
        public HColumn<N,V> SetTtl(int ttl) 
        {
            Column.Ttl = ttl;
            return this;
        }
        public HColumn<N, V> FromThrift(Column c)
        {
            if (c == null)
            {
                throw new NullReferenceException("Column cannot be null");
            }
            Column = c;
            return this;
        }
        public Column ToThrift() 
        {
            return Column;
        }
        public byte[] NameBytes
        {
            get
            {
                return Column.Name != null ? Column.Name : null;
            }
        }
        public byte[] ValueBytes
        {
            get
            {
                return Column.Value != null ? Column.Value : null;
            }
        }
        public HColumn<N,V> Clear()
        {
            Column.Value = null;
            Column.Timestamp = 0;
            Column.Ttl = 0;
            return this;
        }
        public HColumn<N, V> Apply(V value, long clock, int ttl)
        {
            SetValue(value);
            Column.Timestamp = clock;
            Column.Ttl = ttl;
            return this;
        }
        public HColumn<N, V> Apply(Column c)
        {
            Column = c;
            CachedName = default(N);
            CachedValue = default(V);
            return this;
        }
        public override string ToString()
        {
            return string.Format("HColumn({0}={1})", Name, Value);
        }
        public override int GetHashCode()
        {
            int hcode = 0;
            if (Column != null && Column.Name != null)
            {
                hcode += Column.Name.GetHashCode();
                if (Column.Value != null)
                {
                    hcode += Column.Value.GetHashCode();
                }
                if (Column.Timestamp != null)
                {
                    hcode += (int)Column.Timestamp;
                }
            }
            return hcode;
        }
        public override bool Equals(object obj)
        {
            if (obj == null) 
            {
                return false;
            }
            if (obj == this) 
            {
                return true;
            }
            if (obj.GetType() != GetType())
            {
                return false;
            }
            HColumnImpl<N,V> other = (HColumnImpl<N,V>) obj;
            if (Name.Equals(other.Name) && Value.Equals(other.Value) && Clock.Equals(other.Clock))
            {
                return true;
            }
            return false;
        }
    }
}
